﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using NUnit.Framework;
using XcoAppSpaces.Contracts.Messaging;
using XcoAppSpaces.Contracts.Worker;
using XcoAppSpaces.Core;
using Microsoft.Ccr.Core;

namespace test.XcoAppSpaces.Core
{
	[TestFixture]
	public class testXcoAppSpaceExtensions
	{
		[Test]
		public void TestReceiveSequentiallyWithException()
		{
			TestReceiveWithException((space, port, handler) => space.ReceiveSequentially(port, handler));
		}

		[Test]
		public void TestReceiveConcurrentWithException()
		{
			TestReceiveWithException((space, port, handler) => space.Receive(port, handler));
		}


		public void TestReceiveWithException(Action<IXcoAppSpace, Port<int>,Handler<int>> installReceiver)
		{
			using (IXcoAppSpace space = new XcoAppSpace())
			{
				var port = new Port<int>();
				int handlerCalled = 0;
				installReceiver(space, port, x => { handlerCalled++; throw new Exception(); });
				
				var exceptionPort = new Port<Exception>();
				var c = new Causality("c", exceptionPort);
				Dispatcher.AddCausality(c);
				port.Post(1);
				port.Post(2);
				port.Post(3);
				Dispatcher.RemoveCausality(c);

				Thread.Sleep(100);
				Assert.AreEqual(3, handlerCalled);
				Assert.AreEqual(3, exceptionPort.ItemCount);
			}
		}

		[Test]
		public void TestReceiveChoice()
		{
			using (IXcoAppSpace space = new XcoAppSpace())
			{
                var port1 = new Port<int>();
				var port2 = new Port<int>();

				int handler1Called = 0;
				int handler2Called = 0;

				space.ReceiveChoice(port1, port2, x => handler1Called++, y => handler2Called++);

				port2.Post(1);
				Thread.Sleep(100);
				port1.Post(2);
				port2.Post(3);
				port1.Post(4);

				Assert.AreEqual(0, handler1Called);
				Assert.AreEqual(1, handler2Called);
			}
		}

		[Test]
		public void TestReceiveJoined()
		{
			using (IXcoAppSpace space = new XcoAppSpace())
			{
				var port1 = new Port<int>();
				var port2 = new Port<int>();

				int handlerCalled = 0;
				int num1 = 0;
				int num2 = 0;

				space.ReceiveJoined(port1, port2, (x, y) =>
				                                  	{
				                                  		handlerCalled++;
				                                  		num1 = x;
				                                  		num2 = y;
				                                  	});

				port2.Post(1);
				Thread.Sleep(100);
				Assert.AreEqual(0, handlerCalled);
				
				port1.Post(2);
				Thread.Sleep(100);
				Assert.AreEqual(1, handlerCalled);
				Assert.AreEqual(2, num1);
				Assert.AreEqual(1, num2);

				port1.Post(3);
				Thread.Sleep(100);
				Assert.AreEqual(1, handlerCalled);

				port1.Post(4);
				Thread.Sleep(100);
				Assert.AreEqual(1, handlerCalled);

				port2.Post(5);
				Thread.Sleep(100);
				Assert.AreEqual(2, handlerCalled);
				Assert.AreEqual(3, num1);
				Assert.AreEqual(5, num2);
				
				Assert.AreEqual(1, port1.ItemCount);
				Assert.AreEqual(0, port2.ItemCount);
			}
		}

		[Test]
		public void TestReceiveWithIterator()
		{
			using (IXcoAppSpace space = new XcoAppSpace())
			{
				var port1 = new Port<int>();
				yieldPort = new Port<int>();
				received = 0;

				space.ReceiveWithIterator(port1, TestReceiveWithIterator);

				port1.Post(3);

				Thread.Sleep(500);
				Assert.AreEqual(3, received);

				yieldPort.Post(5);

				Thread.Sleep(100);
				Assert.AreEqual(8, received);

			}
		}

		private Port<int> yieldPort;
		private int received = 0;
		private IEnumerator<ITask> TestReceiveWithIterator(int item)
		{
			received += item;

			yield return yieldPort.Receive();
			int yieldedItem = yieldPort;

			received += yieldedItem;
		}
	}
}
